`include "timescale.v"

module onu_mpcp(
	input 		   reset,
	input 		   Rx_clk,
	input  [7:0]   Rxd,

	input 		   Gtx_clk,
	
	input  [4:0]   rx_state,
	input  [15:0]  rx_byte_cnt,
	input 		   rx_fcs_ok,

	input 		   h_tx_data_rd,		//the read signal drive by RX module

	input  [3:0]   rx_frame_type,			//from classifier

	//transmit relative
	input  [5:0]   tx_state,			//not used !!!
	input  [15:0]  tx_byte_cnt,

	output reg [7:0] tx_mpcp_data,
	output reg 	   tx_mpcp_data_ready,

	output 		   tx_grant, tx_grant_timeslot,
	
	output 		   tx_fifo_rd_en,
	output 		   fifo_mpcp_sel,

	
	output reg [31:0] LocalTimeStamp,
	output reg [15:0] LLID,
	input  [47:0]  LOCAL_MAC_ADDR
	);

localparam [47:0] MAC_CONTROL_ADDR = 48'h0180_C200_0001;
  
// // assign 			  tx_fifo_rd_en = h_tx_data_rd;
// assign 			  tx_fifo_rd_en = 1'b0;
// assign 			  fifo_mpcp_sel = 1'b1;

reg 			  report_ready, register_req_ready, register_ack_ready;
reg 	  [7:0]   report_data, register_req_data, register_ack_data;


////////////////////
wire 			  StartTransFinish, StartWaitReady, StartWaitFinish;		//for Transmit FSM
reg 			  StateTransFinish, StateWaitReady, StateWaitFinish;
wire 			  StartIdle, StartRegReq, StartWaitReg, StartWaitGate, StartRegAck, StartData, StartReport;		//for MPCP FSM
reg 			  StateIdle, StateRegReq, StateWaitReg, StateWaitGate, StateRegAck, StateData, StateReport;
////////////////////

always@(*)
begin
  if(StateRegReq)
	{tx_mpcp_data_ready, tx_mpcp_data} = {register_req_ready, register_req_data};
  else if(StateRegAck)
	{tx_mpcp_data_ready, tx_mpcp_data} = {register_ack_ready, register_ack_data};
  else if(StateReport)
	{tx_mpcp_data_ready, tx_mpcp_data} = {report_ready, report_data};
  // else if(StateData)
  // 	// {tx_mpcp_data_ready, tx_mpcp_data} = 9'hz;
  // 	// fifo_mpcp_sel = 1'b1;
  // 	fifo_mpcp_sel = StateData;
  else
	{tx_mpcp_data_ready, tx_mpcp_data} = 9'h000;

  // tx_fifo_rd_en = StateData ? h_tx_data_rd : 1'b0;
  // fifo_mpcp_sel = StateData;
end
assign 			  tx_fifo_rd_en = StateData ? h_tx_data_rd : 1'b0;
assign 			  fifo_mpcp_sel = ~StateData;			//0 is for fifo, 1 for mpcp



//-------------------- gate processing --------------------
// reg 	  [31:0]  LocalTimeStamp;
always@(posedge Rx_clk or posedge reset)				//not use this clk !
begin
  if(reset)
	LocalTimeStamp <= 32'd0;
  else if(rx_frame_type`GATE_DISCOVERY_FRAME | rx_frame_type`GATE_NORMAL_FRAME)
	LocalTimeStamp <= U_onu_mac.U_onu_classifier.mpcp_timestamp;
  else 
	LocalTimeStamp <= LocalTimeStamp + 32'd1;
end

assign 			  tx_grant 	  = LocalTimeStamp == U_onu_mac.U_onu_classifier.mpcp_gate_grant1_starttime; //not precise, because ...
assign 			  tx_grant_timeslot = (LocalTimeStamp >= U_onu_mac.U_onu_classifier.mpcp_gate_grant1_starttime) & (LocalTimeStamp < U_onu_mac.U_onu_classifier.mpcp_gate_grant1_starttime+{16'd0, U_onu_mac.U_onu_classifier.mpcp_gate_grant1_length});    //not a good signal  ...

//-------------------- register processing --------------------
always@(posedge Rx_clk or posedge reset)
begin
  if(reset)
	LLID <= 16'h0000;
  else if(rx_frame_type`REGISTER_FRAME)
	if(U_onu_mac.U_onu_classifier.mpcp_register_flag==8'd3)		//register success
	  LLID <= U_onu_mac.U_onu_classifier.mpcp_register_port;
end


//-------------------- report processing --------------------
// localparam 		  REPORT_FRAME_BUFFER_DEPTH = 32;
// reg 	  [7:0]   report_frame_buffer[REPORT_FRAME_BUFFER_DEPTH-1:0];
always@(posedge Gtx_clk or posedge reset)
begin
  if(reset)
  begin
	report_ready <= 1'b0;
  end
  else if(StateReport & StateWaitReady)						//transmit FSM
  	report_ready <= 1'b1;
  else 
  begin
	if(h_tx_data_rd)
	begin
	  // tx_mpcp_data <= report_frame_buffer[tx_byte_cnt];
	  case(tx_byte_cnt)
		  //DA
		  16'd0 : report_data = MAC_CONTROL_ADDR[47:40];
		  16'd1 : report_data = MAC_CONTROL_ADDR[39:32];
		  16'd2 : report_data = MAC_CONTROL_ADDR[31:24];
		  16'd3 : report_data = MAC_CONTROL_ADDR[23:16];
		  16'd4 : report_data = MAC_CONTROL_ADDR[15:8];
		  16'd5 : report_data = MAC_CONTROL_ADDR[7:0];
		  //SA
		  16'd6 : report_data = LOCAL_MAC_ADDR[47:40];
		  16'd7 : report_data = LOCAL_MAC_ADDR[39:32];
		  16'd8 : report_data = LOCAL_MAC_ADDR[31:24];
		  16'd9 : report_data = LOCAL_MAC_ADDR[23:16];
		  16'd10 : report_data = LOCAL_MAC_ADDR[15:8];
		  16'd11 : report_data = LOCAL_MAC_ADDR[7:0];
		  //Length/Type
		  16'd12 : report_data = 8'h88;
		  16'd13 : report_data = 8'h08;
		  //opcode
		  16'd14 : report_data = 8'h00;
		  16'd15 : report_data = 8'h03;
		  //timestamp
		  16'd16 : report_data = LocalTimeStamp[31:24];
		  16'd17 : report_data = LocalTimeStamp[23:16];
		  16'd18 : report_data = LocalTimeStamp[15:8];
		  16'd19 : report_data = LocalTimeStamp[7:0];
		  //Number of queue sets
		  16'd20 : report_data = 8'h01;			//only one report 
		  //Report bitmap
		  16'd21 : report_data = 8'h01;			//queue #0 is present
		  //Queue #0 Report
		  16'd22 : report_data = 8'h00;
		  16'd23 : report_data = 8'h3f;
		  
		  default: report_data = 8'h00;
	  endcase

if(tx_byte_cnt == 16'd23)	//the last byte of a frame
begin
  report_ready <= 1'b0;
end

	end
  end
end

//-------------------- register_req processing --------------------
// localparam 		  REGISTER_REQ_FRAME_BUFFER_DEPTH = 32;
// reg 	  [7:0]   register_req_frame_buffer[REGISTER_REQ_FRAME_BUFFER_DEPTH-1:0];
always@(posedge Gtx_clk or posedge reset)
begin
  if(reset)
  begin
	register_req_ready <= 1'b0;
  end
  else if(StateRegReq & StateWaitReady)						//transmit FSM
  	register_req_ready <= 1'b1;
  else 
  begin
	if(h_tx_data_rd)
	begin
	  // tx_mpcp_data <= register_req_frame_buffer[tx_byte_cnt];
	  case(tx_byte_cnt)
		  //DA
		  16'd0 : register_req_data = MAC_CONTROL_ADDR[47:40];
		  16'd1 : register_req_data = MAC_CONTROL_ADDR[39:32];
		  16'd2 : register_req_data = MAC_CONTROL_ADDR[31:24];
		  16'd3 : register_req_data = MAC_CONTROL_ADDR[23:16];
		  16'd4 : register_req_data = MAC_CONTROL_ADDR[15:8];
		  16'd5 : register_req_data = MAC_CONTROL_ADDR[7:0];
		  //SA
		  16'd6 : register_req_data = LOCAL_MAC_ADDR[47:40];
		  16'd7 : register_req_data = LOCAL_MAC_ADDR[39:32];
		  16'd8 : register_req_data = LOCAL_MAC_ADDR[31:24];
		  16'd9 : register_req_data = LOCAL_MAC_ADDR[23:16];
		  16'd10 : register_req_data = LOCAL_MAC_ADDR[15:8];
		  16'd11 : register_req_data = LOCAL_MAC_ADDR[7:0];
		  //Length/Type
		  16'd12 : register_req_data = 8'h88;
		  16'd13 : register_req_data = 8'h08;
		  //opcode
		  16'd14 : register_req_data = 8'h00;
		  16'd15 : register_req_data = 8'h04;
		  //timestamp
		  16'd16 : register_req_data = LocalTimeStamp[31:24];
		  16'd17 : register_req_data = LocalTimeStamp[23:16];
		  16'd18 : register_req_data = LocalTimeStamp[15:8];
		  16'd19 : register_req_data = LocalTimeStamp[7:0];
		  //Flags
		  16'd20 : register_req_data = 8'h01;
		  //Pending grants
		  16'd21 : register_req_data = 8'h01;

		  default: register_req_data = 8'h00;
	  endcase

if(tx_byte_cnt == 16'd21)	//the last byte of a frame
begin
  register_req_ready <= 1'b0;
end

	end
  end
end

//-------------------- register_ack processing --------------------
// localparam 		  REGISTER_REQ_FRAME_BUFFER_DEPTH = 32;
// reg 	  [7:0]   register_req_frame_buffer[REGISTER_REQ_FRAME_BUFFER_DEPTH-1:0];
always@(posedge Gtx_clk or posedge reset)
begin
  if(reset)
  begin
	register_ack_ready <= 1'b0;
  end
  else if(StateRegAck & StateWaitReady)
  	register_ack_ready <= 1'b1;
  else 
  begin
	if(h_tx_data_rd)
	begin
	  // tx_mpcp_data <= register_ack_frame_buffer[tx_byte_cnt];
	  case(tx_byte_cnt)
		  //DA
		  16'd0 : register_ack_data = MAC_CONTROL_ADDR[47:40];
		  16'd1 : register_ack_data = MAC_CONTROL_ADDR[39:32];
		  16'd2 : register_ack_data = MAC_CONTROL_ADDR[31:24];
		  16'd3 : register_ack_data = MAC_CONTROL_ADDR[23:16];
		  16'd4 : register_ack_data = MAC_CONTROL_ADDR[15:8];
		  16'd5 : register_ack_data = MAC_CONTROL_ADDR[7:0];
		  //SA
		  16'd6 : register_ack_data = LOCAL_MAC_ADDR[47:40];
		  16'd7 : register_ack_data = LOCAL_MAC_ADDR[39:32];
		  16'd8 : register_ack_data = LOCAL_MAC_ADDR[31:24];
		  16'd9 : register_ack_data = LOCAL_MAC_ADDR[23:16];
		  16'd10 : register_ack_data = LOCAL_MAC_ADDR[15:8];
		  16'd11 : register_ack_data = LOCAL_MAC_ADDR[7:0];
		  //Length/Type
		  16'd12 : register_ack_data = 8'h88;
		  16'd13 : register_ack_data = 8'h08;
		  //opcode
		  16'd14 : register_ack_data = 8'h00;
		  16'd15 : register_ack_data = 8'h06;
		  //timestamp
		  16'd16 : register_ack_data = LocalTimeStamp[31:24];
		  16'd17 : register_ack_data = LocalTimeStamp[23:16];
		  16'd18 : register_ack_data = LocalTimeStamp[15:8];
		  16'd19 : register_ack_data = LocalTimeStamp[7:0];
		  //Flags
		  16'd20 : register_ack_data = 8'h01;	//register success
		  //echoed assigned port
		  16'd21 : register_ack_data = 8'h00;
		  16'd22 : register_ack_data = 8'h03;
		  //echoed sync time
		  16'd23 : register_ack_data = 8'h00;
		  16'd24 : register_ack_data = 8'h01;

		  default: register_ack_data = 8'h00;
	  endcase

if(tx_byte_cnt == 16'd24)	//the last byte of a frame
begin
  register_ack_ready <= 1'b0;
end
	end
  end
end

// -------------------- Transmit FSM (handshake) --------------------
// wire 			  StartIdle, StartWaitReady, StartWaitFinish;
// reg 			  StateIdle, StateWaitReady, StateWaitFinish;

assign 			  StartTransFinish = StateWaitFinish & ~tx_mpcp_data_ready;
assign 			  StartWaitReady = StateTransFinish & (StartRegReq | StartRegAck | StartReport);
assign 			  StartWaitFinish = StateWaitReady & tx_mpcp_data_ready;
// assign 			  StartRegReq = StateIdle & rx_frame_type`GATE_DISCOVERY_FRAME;
// assign 			  StartRegReqPending = StateRegReq & tx_mpcp_data_ready;
always@(posedge Rx_clk or posedge reset)
begin
  if(reset) begin
	StateTransFinish <= 1'b1;
	StateWaitFinish <= 1'b0;
  end
  else begin
	if(StartWaitFinish)
	  StateTransFinish <= 1'b0;
	else if(StartTransFinish)
	  StateTransFinish <= 1'b1;
	
	if(StartWaitFinish)
	  StateWaitReady <= 1'b0;
	else if(StartWaitReady)
	  StateWaitReady <= 1'b1;

	if(StartTransFinish)
	  StateWaitFinish <= 1'b0;
	else if(StartWaitFinish)
	  StateWaitFinish <= 1'b1;
  end
end

// -------------------- MPCP FSM --------------------
assign 			  StartIdle   = StartTransFinish & (StateRegAck | StateReport);
assign 			  StartRegReq = StateIdle & rx_frame_type`GATE_DISCOVERY_FRAME;
assign 			  StartWaitReg = StateRegReq & StartTransFinish;
assign 			  StartWaitGate = StateWaitReg & rx_frame_type`REGISTER_FRAME;
assign 			  StartRegAck = StateWaitGate & rx_frame_type`GATE_NORMAL_FRAME;

assign 			  StartData   = StateIdle & rx_frame_type`GATE_NORMAL_FRAME;
// assign 			  StartReport = StateData & StartTransFinish;
// assign 			  StartReport = StateData & ~tx_fifo_rd_en;				//here should be the fifo empty signal !!(or integrated with TransmitFSM)
assign 			  StartReport = StateData & U_onu_mac.tx_fifo_empty;

always@(posedge Rx_clk or posedge reset)
begin
  if(reset) begin
	StateIdle <= 1'b1;
	//for discovery
	StateRegReq <= 1'b0;
	StateWaitReg <= 1'b0;
	StateWaitGate <= 1'b0;
	StateRegAck <= 1'b0;
	//for bandwidth allocate
	StateData <= 1'b0;
	StateReport <= 1'b0;
  end
  else begin
	if(StartRegReq | StartData)
	  StateIdle <= 1'b0;
	else if(StartIdle)
	  StateIdle <= 1'b1;

	//for discovery
	if(StartWaitReg)
	  StateRegReq <= 1'b0;
	else if(StartRegReq)
	  StateRegReq <= 1'b1;

	if(StartWaitGate)
	  StateWaitReg <= 1'b0;
	else if(StartWaitReg)
	  StateWaitReg <= 1'b1;

	if(StartRegAck)
	  StateWaitGate <= 1'b0;
	else if(StartWaitGate)
	  StateWaitGate <= 1'b1;
	
	if(StartIdle)
	  StateRegAck <= 1'b0;
	else if(StartRegAck)
	  StateRegAck <= 1'b1;

	//for bandwidth allocate
	if(StartReport)
	  StateData <= 1'b0;
	else if(StartData)
	  StateData <= 1'b1;

	if(StartIdle)
	  StateReport <= 1'b0;
	else if(StartReport)
	  StateReport <= 1'b1;
	
  end
end

// always@(posedge Rx_clk or posedge reset)
// begin
//   if(reset) begin
// 	end
//   else
//   begin
// 	if(StartRegReq)
// 	begin
	  
// 	end
//   end
	
// always@(posedge Rx_clk or posedge reset)
// begin
//   if(StartRegisterReq)
//   begin
// 	//send register_req frame
	




// initial
// begin
//   #100  register_ack_ready = 1;

// end

endmodule // onu_mpcp
